package com.cskaoyan.javase.object._3equals;

/**
 * equals方法的声明，比较简单
 * public boolean equals(Object obj) {
 *         return (this == obj);
 * }
 * 作用： equals有相等的意思，所以这个方法用来判断两个对象是否相等（方法的调用者和方法的实参对象）
 * 既然是判断相等，就需要一个比较的规则，说白了就是equals方法的实现（方法体）就是该方法判断对象相等的比较规则
 *
 * Object类当中默认实现：直接双等号连接两个引用，比较对象的地址值。只有当两个引用完全指向同一个对象时，该方法才会返回true 说白了，这个规则很苛刻，只有对象自己才和自己相等。
 * 一般情况下，我们用不到这么苛刻的规则，我们需要自己重写equals方法。
 *
 * 我们认为的对象相等：
 * 1.同一种类型的对象，行为肯定是一致的。但是属性是有区别的。所以只要两个同类型的对象，它的属性值再一样，就可以认为是对象相等的。
 * 2.不是同一种类型的对象，行为和属性差异都比较大，没有可比性。直接返回false，认定不相等。
 *
 * 当然JDK的开发者，早就为我们规定好了，重写equals方法的规则，叫重写方法的“常规协定”
 * 1. 自反性：对于任何非空引用值 x，`x.equals(x) `都应返回 true
 * 自己和自己比，应该返回true。
 *
 * 2. 排他性：当比对的不是同种类型的对象或者是一个null时，默认返回false
 *
 * 3. 对称性：对于任何非空引用值 x 和 y，当且仅当` y.equals(x)` 返回 true 时，x.equals(y) 才应返回true
 *  x=y时, y=x
 *
 * 4. 传递性：对于任何非空引用值 x、y 和 z，如果` x.equals(y) `返回 true，并且 `y.equals(z)` 返回 true，那么`x.equals(z)` 应返回 true。
 * x=y,y=z --> x=z
 *
 * 5. 一致性：对于任何非空引用值 x 和 y，多次调用 `x.equals(y) `始终返回 true 或始终返回 false。
 *
 * 细节问题:
 * 1.equals方法可以自动生成,仍然用alt+insert
 * 2.在做排它性时,可以有两种方式:
 *      a.getClass判断,比较苛刻,只允许相同类型对象去比较.
 *      b.instanceof判断
 *      引用名 instanceof 类名/接口名
 *      判断引用指向的对象 是否是后面类名的对象或者子类对象
 *      如果引用指向null,直接结果就是false
 *      所以如果用instanceof做排它性判断,就允许传入子类对象
 *
 * 3.浮点数在比较大小时,一般情况下用"=="是足够的
 * 但浮点数有规格化 非规格化的区别,它还有无穷大 无穷小 非数(NaN)的概念
 * 为了保证程序稳定可靠,所以更推荐用包装类当中compare方法去比较浮点数的大小
 * 1. Float.compare(float a,float b)
 * 2. Double.compare(double a,doublet b)
 * 方法的返回值小于0,说明a<b
 * 方法的返回值大于0,说明a>b
 * 方法的返回值等于0,说明a=b
 *
 * 4.如果类中的成员变量有引用数据类型,那么也需要调用该类型的equals方法(有必要的情况下还需要重写)
 * 当然String不需要,因为它已经重写过了(比较字符串的内容)
 *
 *
 * @since 09:50
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        Computer c1 = new Computer(5999, 15.6, 10);
        Computer c2 = new Computer(5999, 15.6, 10);
        System.out.println(c1 == c2); // false
        System.out.println(c1.equals(c2)); // true

        LenovoComputer c3 = new LenovoComputer(5999, 15.6, 10);
        System.out.println(c1.equals(c3));

    }
}
class Computer {
    private double price;
    double size;
    int weight;

    public Computer() {
    }

    public Computer(double price, double size, int weight) {
        this.price = price;
        this.size = size;
        this.weight = weight;
    }

    /*@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Computer)) return false;

        Computer computer = (Computer) o;

        if (Double.compare(computer.price, price) != 0) return false;
        if (Double.compare(computer.size, size) != 0) return false;
        return weight == computer.weight;
    }*/

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Computer computer = (Computer) o;

        if (Double.compare(computer.price, price) != 0) return false;
        if (Double.compare(computer.size, size) != 0) return false;
        return weight == computer.weight;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        temp = Double.doubleToLongBits(price);
        result = (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(size);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        result = 31 * result + weight;
        return result;
    }

    // 重写equals方法
    /*@Override
    public boolean equals(Object obj) {
        // 1.自反性 应该判断等于,因为只要等于就一定返回true
        if (this == obj) {
            return true;
        }
        // 2.排它性,排除null元素和不是同类型的对象
        if (obj == null || this.getClass() != obj.getClass()) {
            // 不是同一个类型或者是null,直接返回false
            return false;
        }

        // 方法如果运行到这里如果还没结束
        *//*
            说明:
            obj引用指向的对象,一定不是null
            而且它的类型一定和当前对象一致
         *//*
        // 对称性,传递性,一致性只需要按照成员变量的取值去比较对象相等,就可以自动满足了
        // 接下来,就开始按照成员变量的取值,去判断对象相等
        // 1.首先需要对obj引用做强转
        Computer target = (Computer) obj;
        // price
        if (this.price != target.price) {
            return false;
        }
        // size
        if (this.size != target.size) {
            return false;
        }
        // weight
        return this.weight == target.weight;
    }*/
}

class LenovoComputer extends Computer{
    public LenovoComputer() {
    }

    public LenovoComputer(double price, double size, int weight) {
        super(price, size, weight);
    }
}